import sys
import socket
import threading
import queue
import platform
from PyQt5.QtWidgets import (
    QApplication, QWidget, QLabel, QLineEdit, QTextEdit, QPushButton, 
    QVBoxLayout, QHBoxLayout, QComboBox
)
from PyQt5.QtCore import pyqtSignal, QObject
import pyttsx3

# Volume control backends
if platform.system() == "Windows":
    from comtypes import CLSCTX_ALL
    from pycaw.pycaw import AudioUtilities, IAudioEndpointVolume
elif platform.system() == "Linux":
    import subprocess
else:
    # Fallback no-op
    pass

class VolumeController:
    def __init__(self):
        self.system = platform.system()
        if self.system == "Windows":
            devices = AudioUtilities.GetSpeakers()
            interface = devices.Activate(
                IAudioEndpointVolume._iid_, CLSCTX_ALL, None
            )
            self.volume = interface.QueryInterface(IAudioEndpointVolume)
        # Linux will use amixer calls

    def volume_up(self):
        if self.system == "Windows":
            current = self.volume.GetMasterVolumeLevelScalar()
            new = min(current + 0.1, 1.0)
            self.volume.SetMasterVolumeLevelScalar(new, None)
        elif self.system == "Linux":
            subprocess.call(["amixer", "-D", "pulse", "sset", "Master", "10%+"],
                            stdout=subprocess.DEVNULL)

    def volume_down(self):
        if self.system == "Windows":
            current = self.volume.GetMasterVolumeLevelScalar()
            new = max(current - 0.1, 0.0)
            self.volume.SetMasterVolumeLevelScalar(new, None)
        elif self.system == "Linux":
            subprocess.call(["amixer", "-D", "pulse", "sset", "Master", "10%-"],
                            stdout=subprocess.DEVNULL)

class SocketWorker(QObject):
    received = pyqtSignal(str)
    connected = pyqtSignal()
    disconnected = pyqtSignal()
    def __init__(self, ip, port):
        super().__init__()
        self.ip = ip
        self.port = port
        self.sock = None
        self.send_queue = queue.Queue()
        self._stop_event = threading.Event()

    def start(self):
        threading.Thread(target=self._run, daemon=True).start()
        threading.Thread(target=self._send_loop, daemon=True).start()

    def _run(self):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.sock.connect((self.ip, int(self.port)))
            self.connected.emit()
            while not self._stop_event.is_set():
                data = self.sock.recv(1024)
                if not data:
                    break
                hex_str = data.hex().upper()
                self.received.emit(hex_str)
        except Exception as e:
            self.received.emit(f"Error: {e}")
        finally:
            self.disconnected.emit()

    def _send_loop(self):
        while not self._stop_event.is_set():
            try:
                msg = self.send_queue.get(timeout=0.5)
                if msg is None:
                    break
                if self.sock:
                    self.sock.send(msg.encode('gb2312', errors='ignore'))
            except queue.Empty:
                continue
            except Exception as e:
                self.received.emit(f"Send error: {e}")
                break

    def send(self, text):
        self.send_queue.put(f"#{text}")

    def close(self):
        self._stop_event.set()
        self.send_queue.put(None)
        if self.sock:
            self.sock.close()
            self.sock = None

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Socket & TTS Controller")
        self.resize(800, 600)

        # Socket UI
        self.ip_input = QLineEdit("192.168.1.65")
        self.port_input = QLineEdit("50000")
        self.connect_btn = QPushButton("Connect")
        self.send_input = QLineEdit()
        self.send_btn = QPushButton("Send")
        self.close_btn = QPushButton("Close")
        self.log_area = QTextEdit()
        self.log_area.setReadOnly(True)

        # TTS & Volume UI
        self.tts_input = QLineEdit()
        self.tts_btn = QPushButton("Speak")
        self.voice_selector = QComboBox()
        self.vol_up_btn = QPushButton("Vol +")
        self.vol_down_btn = QPushButton("Vol -")

        # Layouts
        top_layout = QHBoxLayout()
        top_layout.addWidget(QLabel("IP:")); top_layout.addWidget(self.ip_input)
        top_layout.addWidget(QLabel("Port:")); top_layout.addWidget(self.port_input)
        top_layout.addWidget(self.connect_btn)
        top_layout.addWidget(self.close_btn)

        send_layout = QHBoxLayout()
        send_layout.addWidget(QLabel("Send Text:")); send_layout.addWidget(self.send_input); send_layout.addWidget(self.send_btn)

        tts_layout = QHBoxLayout()
        tts_layout.addWidget(QLabel("TTS Text:")); tts_layout.addWidget(self.tts_input)
        tts_layout.addWidget(self.voice_selector); tts_layout.addWidget(self.tts_btn)
        tts_layout.addWidget(self.vol_down_btn); tts_layout.addWidget(self.vol_up_btn)

        main_layout = QVBoxLayout(self)
        main_layout.addLayout(top_layout)
        main_layout.addLayout(send_layout)
        main_layout.addWidget(self.log_area)
        main_layout.addLayout(tts_layout)

        # Initialize
        self.socket_worker = None
        self.engine = pyttsx3.init()
        self.vol_ctrl = VolumeController()

        # Populate voices
        for voice in self.engine.getProperty('voices'):
            self.voice_selector.addItem(voice.name, voice.id)

        # Connect signals
        self.connect_btn.clicked.connect(self.on_connect)
        self.send_btn.clicked.connect(self.on_send)
        self.close_btn.clicked.connect(self.on_close)
        self.tts_btn.clicked.connect(self.on_tts)
        self.vol_up_btn.clicked.connect(self.vol_ctrl.volume_up)
        self.vol_down_btn.clicked.connect(self.vol_ctrl.volume_down)

    def log(self, message: str):
        from datetime import datetime
        ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        self.log_area.append(f"{ts} {message}")

    def on_connect(self):
        if self.socket_worker:
            self.log("Already connected")
            return
        ip = self.ip_input.text()
        port = self.port_input.text()
        self.socket_worker = SocketWorker(ip, port)
        self.socket_worker.received.connect(lambda msg: self.log(f"Received: {msg}"))
        self.socket_worker.connected.connect(lambda: self.log("Connected"))
        self.socket_worker.disconnected.connect(lambda: self.on_disconnected())
        self.socket_worker.start()

    def on_send(self):
        if not self.socket_worker:
            self.log("Not connected")
            return
        text = self.send_input.text()
        self.socket_worker.send(text)
        self.log(f"Sent: #{text}")

    def on_close(self):
        if self.socket_worker:
            self.socket_worker.close()

    def on_disconnected(self):
        self.log("Disconnected")
        self.socket_worker = None

    def on_tts(self):
        text = self.tts_input.text()
        voice_id = self.voice_selector.currentData()
        self.engine.setProperty('voice', voice_id)
        self.engine.say(text)
        self.engine.runAndWait()
        self.log(f"Spoke: {text}")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())